<?php

namespace CtyunOss\cors;

use AlexTartan\Array2Xml\XmlToArray;
use CtyunOss\config\OssConfig;
use CtyunOss\config\Request;
use CtyunOss\exceptions\OssHttpException;
use CtyunOss\exceptions\OssSignException;
use CtyunOss\struct\Error;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\VarDumper\VarDumper;

/**
 * 接口请求处理类
 * @package CtyunOss\cors
 *
 * @author  Violet_Ice紫冰 <violetice@aliyun.com>
 * @create  2020-06-24 16:23:08
 */
class OssHttp
{
    /** @var Request 请求参数 */
    private $request;

    /** @var OssConfig 天翼云OSS配置 */
    private $config;

    public function __construct(Request $request, OssConfig $config)
    {
        $this->request = $request;
        $this->config  = $config;
    }

    /**
     * 请求天翼云OSS接口
     *
     * @param bool $notBucket
     *
     * @return string
     * @throws OssHttpException
     * @throws \AlexTartan\Array2Xml\Exception\ConversionException
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function send(bool $notBucket = false)
    {
        $request = $this->request;

        $client = HttpClient::create(['http_version' => '2.0']);
        $url    = $this->getUrl($this->request->getBucket());
        if ($this->config->debug) {
            VarDumper::dump($request->getMethod() . ' ' . $url);
        }

        $response = $client->request($request->getMethod(), $url, $this->getOptions($notBucket));
        $res      = $response->getContent(false);
        if ($this->config->debug) {
            VarDumper::dump('Api Response: ' . $res);
            VarDumper::dump('Api Code: ' . $response->getStatusCode());
            VarDumper::dump('Api Debug: ' . $response->getInfo('debug'));
        }
        if ($response->getStatusCode() !== 200) {
            if ($res === '') {
                return $res;
            }
            $data = (new XmlToArray)->buildArrayFromString($res);
            throw new OssHttpException(new Error($data['Error']));
        }

        return $res;
    }

    /**
     * 获取签名后的 Authorization
     *
     * @param OssConfig $config
     * @param Request $request
     *
     * @return string
     * @throws OssSignException
     */
    private function getAuthorization(OssConfig $config, Request $request): string
    {
        switch ($this->config->getSignVersion()) {
            case OssConfig::SIGN_V2:
            {
                $sign          = new SignV2($request, $config);
                $Authorization = $sign->sign();
                break;
            }
            case OssConfig::SIGN_V4:
            {
                throw new OssSignException('暂不支持V4版签名算法');
                break;
            }
            default:
                throw new OssSignException('不支持的签名算法');
                break;
        }

        return $Authorization;
    }

    /**
     * 组装接口地址
     *
     * @param string $bucket
     *
     * @return string
     */
    private function getUrl(string $bucket)
    {
        $request = $this->request;
        return 'https://' . ($bucket ? $bucket . '.' : '') . $request->getHost() . $request->getUrl();
    }

    /**
     * 生成请求Option
     *
     * @param bool $notBucket
     *
     * @return array
     * @throws OssSignException
     */
    private function getOptions(bool $notBucket)
    {
        $options = [];

        $bucket = $this->request->getBucket();
        if ($notBucket) {
            $this->request->setBucket('');
        }
        $authorization = $this->getAuthorization($this->config, $this->request);
        $this->request->setBucket($bucket);

        if ($this->config->debug) {
            VarDumper::dump('Authorization: ' . $authorization);
        }

        $headers            = $this->request->getOptions()['headers'] ?? [];
        $options['headers'] = array_merge(
            $this->request->getHeader(),
            $this->request->getXHeader(),
            $headers,
            [
                'Date'          => $this->request->getDate()->toRfc7231String(),
                'Authorization' => $authorization
            ]
        );

        if (!empty($this->request->getQuery())) {
            $options['query'] = $this->request->getQuery();
        }

        return array_merge($options, $this->request->getOptions());
    }
}